package lab.cadl.lirui.markdown.core.renderers

import lab.cadl.lirui.markdown.core.exceptions.MarkdownException
import lab.cadl.lirui.markdown.core.model.*
import java.io.BufferedWriter
import java.io.File
import java.nio.file.Paths

class HtmlVisitor(private val writer: BufferedWriter, private val renderOption: RenderOption) : AbstractVisitor() {
    private val htmlOption: HtmlOption? = renderOption.customOption as HtmlOption?

    override fun visit(document: MarkdownDocument) {
        writer.write("""<html>
            <head>
            <title>${renderOption.originName}</title>
            </head>
            <body>
        """.trimIndent())
        visitChildren(document)
        writer.write("""
                    </body>
                    </html>
                """.trimIndent())
    }

    override fun visit(section: Section) {
        section.heading?.accept(this)

        visitChildren(section)
    }

    override fun visit(heading: Heading) {
        if (heading.level <= 0 || heading.level > 6) {
            throw MarkdownException("heading level should be in [1, 6]")
        }

        writer.write("<h${heading.level}>${heading.fullSeqString()}&nbsp;")
        visitChildren(heading)
        writer.write("</h${heading.level}>")
    }

    override fun visit(paragraph: Paragraph) {
        writer.write("<p>")
        visitChildren(paragraph)
        writer.write("</p>")
    }

    override fun visit(orderedList: OrderedList) {
        writer.write("<ol>")
        visitChildren(orderedList)
        writer.write("</ol>")
    }

    override fun visit(bulletList: BulletList) {
        writer.write("<ul>")
        visitChildren(bulletList)
        writer.write("</ul>")
    }

    override fun visit(listItem: ListItem) {
        writer.write("<li>")
        visitChildren(listItem)
        writer.write("</li>")
    }

    override fun visit(indentCodeBlock: IndentCodeBlock) {
        writer.write("<div><code>")
        visitChildren(indentCodeBlock)
        writer.write("</code></div>")
    }

    override fun visit(fencedCodeBlock: FencedCodeBlock) {
        writer.write("<div><code>")
        visitChildren(fencedCodeBlock)
        writer.write("</code></div>")
    }

    override fun visit(imageBlock: ImageBlock) {
        if (htmlOption?.fixBlockImageWidth == null) {
            writer.write("<p><img src='${imageBlock.url}' alt=${imageBlock.title}></p>")
        } else {
            writer.write("<p><img src='${imageBlock.url}' width='${htmlOption.fixBlockImageWidth}' alt=${imageBlock.title}></p>")
        }
        if (htmlOption?.useSectionImageCounter == true) {
            writer.write("<p>图&nbsp;${imageBlock.rootSection?.seq}-${imageBlock.sectionCounter}&nbsp;&nbsp;${imageBlock.title}</p>")
        } else {
            writer.write("<p>图&nbsp;${imageBlock.documentCounter}&nbsp;&nbsp;${imageBlock.title}</p>")
        }
    }

    override fun visit(blockQuote: BlockQuote) {
        super.visit(blockQuote)
    }

    override fun visit(thematicBreak: ThematicBreak) {
        super.visit(thematicBreak)
    }

    override fun visit(hardLineBreak: HardLineBreak) {
        super.visit(hardLineBreak)
    }

    override fun visit(text: Text) {
        writer.write(text.content)
    }

    override fun visit(image: Image) {
        super.visit(image)
    }

    override fun visit(code: Code) {
        super.visit(code)
    }

    override fun visit(emphasis: Emphasis) {
        super.visit(emphasis)
    }
}

class HtmlOption : CustomOption {
    var useSectionImageCounter: Boolean = true
    var fixBlockImageWidth: Int? = 400
}

class HtmlRenderer : DocumentRenderer {
    override fun render(doc: MarkdownDocument, option: RenderOption): File {
        val outputFile = Paths.get(option.outputDir, option.originName + ".html").toFile()
        outputFile.bufferedWriter().use { HtmlVisitor(it, option).visit(doc) }
        return outputFile
    }

    override fun defaultCustomOption(): CustomOption {
        return HtmlOption()
    }
}